/**
 * @file h264_set.h
 * @author your name (you@domain.com)
 * @brief h264 sps pps sei相关
 * @version 0.1
 * @date 2025-08-03
 * 
 * @copyright Copyright (c) 2025
 * 
 */
#ifndef H264_SET_H
#define H264_SET_H
#include <stdint.h>
#include "h264_frame.h"

enum profile_e
{
    PROFILE_BASELINE = 66,
    PROFILE_MAIN     = 77,
    PROFILE_HIGH    = 100,
    PROFILE_HIGH10  = 110,
    PROFILE_HIGH422 = 122,
    PROFILE_HIGH444_PREDICTIVE = 244,
};

#define BIT_DEPTH 8

enum chroma_format_e
{
    CHROMA_400 = 0,
    CHROMA_420 = 1,
    CHROMA_422 = 2,
    CHROMA_444 = 3,
};

struct sps_param_t {
    int profile_idc;
    uint8_t b_constraint_set0_flag;     // profile_idc == PROFILE_BASELINE set 1 otherwise 0
    uint8_t b_constraint_set1_flag;     // profile_idc <= PROFILE_MAIN set 1 otherwise 0
    uint8_t b_constraint_set2_flag;     // default 0
    uint8_t b_constraint_set3_flag;     // default 0
    int level_idc;
    int id;                             // SPS ID 后续编码引用
    int chroma_format_idc;              // 0:single, 1:420, 2:422, 3:444

    // 0不跳过变换和量化 1表示对于当前的宏块或块，亮度分量的量化系数经过特殊处理后直接跳过变换过程
    int b_qpprime_y_zero_transform_bypass;
    int seq_scaling_matrix_present_flag;        // 缩放矩阵
    int i_log2_max_frame_num;           // log2计算方式帧最大数量
    int pic_order_cnt_type;             // POC计算方式 0 可能存在乱序和lsb计算 1 2
    int i_num_ref_frames;               // 可能的参考帧最大数量
    int b_gaps_in_frame_num_value_allowed;  // 这个句法元素等于 1 时，表示允许句法元素 frame_num 可以不连续 为了丢帧时候的处理
    int i_mb_width;                     // 宏块宽度
    int i_mb_height;                    // 宏块高度
    int b_frame_mbs_only;               // 0:frame or feild ,1:frame

    /**
     * 当mb_adaptive_frame_field_flag为 1 时，编码器可以根据图像各部分的特性，
     * 灵活地在帧编码和场编码模式之间为每个宏块对进行选择。例如，对于运动较为复杂的区域，
     * 可以选择场编码，能更好地捕捉运动信息；对于相对静止的区域，则可以采用帧编码，
     * 以提高编码效率。如果该标志位为 0，就不存在帧编码和场编码之间的切换，
     * 所有宏块都将按照固定的帧编码或场编码方式进行编码
     * 因此在b_frame_mbs_only为0时 b_mb_adaptive_frame_field应为1
     */
    int b_mb_adaptive_frame_field;      // 0:frame,1:adaptive

    /**
     * direct_8x8_inference_flag 为 1 时：
     * 采用基于块的推导模式。在这种模式下，8x8 块中的每个 4x4 子块会独立地推导其运动信息。
     * 这种方式可以更精细地反映图像中不同区域的运动情况，适合处理运动复杂、细节丰富的图像内容。
     * 例如，在处理包含快速运动物体或者不规则运动的场景时，基于块的推导模式能够更准确地捕捉运动细节，
     * 从而提高预测的准确性。
     * direct_8x8_inference_flag 为 0 时：
     * 使用基于宏块的推导模式。在该模式下，整个 8x8 块共享相同的运动信息，以宏块为单位进行运动信息的推导。
     * 这种方式计算相对简单，减少了编码时的计算量和数据量，适用于运动较为平缓、图像内容相对简单的场景。
     * 例如，在处理背景变化缓慢、物体运动规律的视频画面时，基于宏块的推导模式可以在保证一定预测精度的前提下，有效降低编码复杂度。
     * 
     */
    int b_direct8x8_inference;          // 0:no,1:yes
    int b_crop;                         // 0:no,1:yes 裁剪标志
    int crop_left;                      // 裁剪左边界
    int crop_right;                     // 裁剪右边界
    int crop_top;                       // 裁剪上边界
    int crop_bottom;                    // 裁剪下边界
    int vui_parameters_present_flag;    // 0:no,1:yes
    int b_vui;                          // 0:no,1:yes 是否包含VUI参数
    struct
    {
        int b_aspect_ratio_info_present;    // 像素横纵比相关信息
        int i_sar_width;
        int i_sar_height;

        int b_overscan_info_present;        // 过扫描信息。一般没有
        int b_overscan_info;

        int b_signal_type_present;          // 视频信号类型信息
        int i_vidformat;                    // 默认为5
        int b_fullrange;
        int b_color_description_present;
        int i_colorprim;
        int i_transfer;
        int i_colmatrix;

        int b_chroma_loc_info_present;      // 色度定位信息
        int i_chroma_loc_top;
        int i_chroma_loc_bottom;

        int b_timing_info_present;          // 时间信息
        uint32_t i_num_units_in_tick;
        uint32_t i_time_scale;
        int b_fixed_frame_rate;

        int b_nal_hrd_parameters_present;   // nal层hrd参数，hrd码率控制信息
        int b_vcl_hrd_parameters_present;   // vcl层hrd参数

        struct
        {
            int i_cpb_cnt;
            int i_bit_rate_scale;
            int i_cpb_size_scale;
            int i_bit_rate_value;
            int i_cpb_size_value;
            int i_bit_rate_unscaled;
            int i_cpb_size_unscaled;
            int b_cbr_hrd;

            int i_initial_cpb_removal_delay_length;
            int i_cpb_removal_delay_length;
            int i_dpb_output_delay_length;
            int i_time_offset_length;
        } hrd;

        int b_pic_struct_present;                       //  SPS 或者补充增强信息（SEI）消息中包含了图像结构的相关信息
        int b_bitstream_restriction;                    // 码流限制参数
        int b_motion_vectors_over_pic_boundaries;       // 运动矢量参考是否超出图像边界
        int i_max_bytes_per_pic_denom;                  // 限定图像比特数
        int i_max_bits_per_mb_denom;                    // 限定宏块比特数
        int i_log2_max_mv_length_horizontal;            // 水平方向最大运动矢量长度
        int i_log2_max_mv_length_vertical;              // 垂直方向最大运动矢量长度
        int i_num_reorder_frames;                       // 最大重排序帧数
        int i_max_dec_frame_buffering;                  // 假设解码器最大解码帧缓存数

    } vui;
};

/** pps param */
struct pps_param_t {
    int i_id;
    int i_sps_id;

    int b_cabac;

    int b_pic_order;            // 1 图像顺序数有关的语法元素将出现于slice header中, 0 slice header中不会出现与图像顺序数有关的语法元素
    int i_num_slice_groups;     // 切片组数量，默认为1，slice group用来将图像分为不同的片组，优化网络传输

    int i_num_ref_idx_l0_default_active;        // 参考帧队列0的活跃参考帧数量 list0 前向预测帧队列 P B帧使用
    int i_num_ref_idx_l1_default_active;        // 参考帧队列1的活跃参考帧数量 list1 后向预测帧队列 B帧使用

    int b_weighted_pred;                        // 0 加权的预测不应用于 P 和 SP 条带。weighted_pred_flag, 1 表示在 P 和 SP条带中应使用加权的预测
    int b_weighted_bipred;                      // 0 加权的预测不应用于 B 条带。weighted_pred_flag, 1 表示在B条带中具体指明加权预测 2 表示Bslice使用隐含的加权预测

    int i_pic_init_qp;                          // 初始qp，相对于26
    int i_pic_init_qs;                          // 在 SP 或 SI 条带中的所有宏块的 SliceQSY 初始值减 26

    int i_chroma_qp_index_offset;               // 色度分离QP值的表格中寻找，色度分量而应加到参数 QPY 和 QSY 上的偏移

    int b_deblocking_filter_control;            // 去块滤波相关语法元素是否出现在slice header中
    
    // 等于 0 表示帧内预测允许使用所有已经编码的宏块，1表示启用约束只能帧内的宏块
    int b_constrained_intra_pred;               

    int b_redundant_pic_cnt;                    // 冗余图像计数语法元素是否出现

    int b_transform_8x8_mode;                   // 0 表示不使用8x8变换，1 表示可以使用，使用之后计算量更大
};

void init_sps(struct sps_param_t *sps, h264_frame_t *frame);
void init_pps(struct pps_param_t *pps, h264_frame_t *frame);
#endif